using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Profiling;

namespace SpriteEffectSystem
{
    /// <summary>
    /// Sprite 特效控制器
    /// </summary>
    public class SpriteEffectController : MonoBehaviour
    {
        /// <summary>
        /// 渲染器模板 GameObject，多次复制这个物体来创建更多 Effect。
        /// </summary>
        public SpriteRenderer RenderTemplate;

        /// <summary>
        /// 透明度系数
        /// </summary>
        [Range(0f, 1f)]
        public float GlobalAlpha = 1f;

        /// <summary>
        /// 全局播放速度
        /// </summary>
        public float SpeedRatio = 1f;

        /// <summary>
        /// 自动加载的目标目录
        /// </summary>
        public string[] ResourcesDirection = { "Effects" };

        /// <summary>
        /// EffectData 对象池，池中的下标作为 EffectData 的 ID 使用。
        /// </summary>
        private readonly List<Effect> _effectDataPool = new();

        /// <summary>
        /// 未启用的 EffectData 的 ID。
        /// </summary>
        private readonly Queue<int> _unPlayingIds = new();

        /// <summary>
        /// 播放中的 EffectData 的 ID。
        /// </summary>
        private readonly List<int> _playingIds = new();

        /// <summary>
        /// 动画库
        /// </summary>
        private readonly Dictionary<string, SpriteEffectAnima> _animaLibary = new();

        private void Awake()
        {
            RenderTemplate.gameObject.SetActive(false);
            if (!RenderTemplate.TryGetComponent<SpriteRenderer>(out var sr))
                Debug.LogError("SpriteEffectController 的渲染器物体必须包含 SpriteRender 组件。");
        }

        private void Update()
        {
            for (var index = 0; index < _playingIds.Count; index++)
            {
                var id = _playingIds[index];
                if (!EffectUpdate(id, Time.deltaTime)) continue;
                // STOP EFFECT
                StopEffectByInPlayingListIndex(index);
                index--;
            }
        }

        /// <summary>
        /// 注册一个动画
        /// </summary>
        public void RegisterAnima(SpriteEffectAnima anima)
        {
            if (_animaLibary.ContainsKey(anima.name))
            {
                Debug.LogError($"动画 {anima.name} 已经存在或名称重复，不能重复注册");
                return;
            }

            _animaLibary.Add(anima.name, anima);
        }

        /// <summary>
        /// 播放一个特效，返回这个特效的 ID。
        /// </summary>
        public int PlayEffect(string animaId, EffectPlayParam param)
        {
            var eid = UseEffectData();

            // 属性设置
            ResetEffectData(eid, animaId, param);
            ResetTransformData(eid, param);
            ResetRenderData(eid, animaId, param);

            // Active
            _effectDataPool[eid].GameObject.SetActive(true);
            return eid;
        }

        /// <summary>
        /// 结束一个动画。
        /// </summary>
        public void StopEffect(int id)
        {
            var index = _playingIds.IndexOf(id);
            if (index >= 0) StopEffectByInPlayingListIndex(index);
        }

        /// <summary>
        /// 根据在 _playingIds 中的下标结束一个动画
        /// </summary>
        private void StopEffectByInPlayingListIndex(int inPlayingListIndex)
        {
            var eid = _playingIds[inPlayingListIndex];
            _playingIds.RemoveAt(inPlayingListIndex);
            _effectDataPool[eid].Transform.SetParent(transform);
            _effectDataPool[eid].GameObject.SetActive(false);
            _unPlayingIds.Enqueue(eid);
        }

        /// <summary>
        /// 从对象池中启用或创建一个特效，并将其加入到播放中列表
        /// </summary>
        private int UseEffectData()
        {
            while (true)
            {
                var tryOk = _unPlayingIds.TryDequeue(out var eid);
                
                switch (tryOk)
                {
                    case true when !_effectDataPool[eid].Transform:
                        continue;
                    case false:
                    {
                        var sr = Instantiate(RenderTemplate, transform);
                        eid = _effectDataPool.Count;
                        _effectDataPool.Add(new Effect(sr));
                        break;
                    }
                }

                _playingIds.Add(eid);
                
                return eid;
            }
        }
        
        /// <summary>
        /// 准备播放特效前对动画数据的初始化工作
        /// </summary>
        private void ResetEffectData(int id, string animaId, EffectPlayParam param)
        {
            var ed = _effectDataPool[id];
            var anima = GetAnimaOrLoad(animaId);
            ed.Frames = anima.Frames;
            ed.AnimaLength = anima.Frames.Length;
            ed.FramesInterval = 1f / anima.Fps;
            ed.CurrentFrame = 0;
            ed.Timer = 0;
            ed.Speed = param.Speed;
            ed.Loop = param.Loop;
            _effectDataPool[id] = ed;
        }

        /// <summary>
        /// 准备播放特效前对动画 Transform 的初始化工作
        /// </summary>
        private void ResetTransformData(int id, EffectPlayParam param)
        {
            var trans = _effectDataPool[id].Transform;
            if (param.AttachTarget)
            {
                trans.SetParent(param.AttachTarget);
                trans.localPosition = param.Position;
                trans.localRotation = param.Rotation;
            }
            else
            {
                trans.position = param.Position;
                trans.rotation = param.Rotation;
            }

            trans.localScale = param.Scale;
        }

        /// <summary>
        /// 准备播放特效前对动画 SpriteRender 的初始化工作
        /// </summary>
        private void ResetRenderData(int id, string animaId, EffectPlayParam param)
        {
            var render = _effectDataPool[id].Render;

            var color = param.Color ?? RenderTemplate.color;
            color.a *= GlobalAlpha;
            render.color = color;
            render.sharedMaterial = param.Material ? param.Material : RenderTemplate.sharedMaterial;
            render.gameObject.layer = param.Layer ?? RenderTemplate.gameObject.layer;

            render.sprite = GetAnimaOrLoad(animaId).Frames[0];
            render.flipX = param.FilpX ?? RenderTemplate.flipX;
            render.flipY = param.FilpY ?? RenderTemplate.flipY;
            render.sortingOrder = param.OrderInLayer ?? RenderTemplate.sortingOrder;
            render.sortingLayerID = param.SortingLayerId ?? RenderTemplate.sortingLayerID;
            render.renderingLayerMask = param.RenderLayerMask ?? RenderTemplate.renderingLayerMask;
        }

        /// <summary>
        /// 更新画面，如果播放完成返回 true
        /// </summary>
        private bool EffectUpdate(int id, float deltaTime)
        {
            var isEnd = false;
            var ed = _effectDataPool[id];
            var oldFrameIndex = ed.CurrentFrame;

            ed.Timer += deltaTime * SpeedRatio * ed.Speed;
            while (ed.Timer >= ed.FramesInterval)
            {
                ed.Timer -= ed.FramesInterval;
                ed.CurrentFrame++;
                if (ed.CurrentFrame != ed.AnimaLength) continue;
                if (ed.Loop)
                {
                    ed.CurrentFrame = 0;
                }
                else
                {
                    isEnd = true;
                    break;
                }
            }
            
            _effectDataPool[id] = ed;
            
            if (isEnd) return true;
            
            if (oldFrameIndex != ed.CurrentFrame)
                ed.Render.sprite = ed.Frames[ed.CurrentFrame];
            
            return false;
        }

        /// <summary>
        /// 获取一个动画，若库中没有则尝试加载
        /// </summary>
        private SpriteEffectAnima GetAnimaOrLoad(string id)
        {
            if (_animaLibary.TryGetValue(id, out var anima)) return anima;

            foreach (var dir in ResourcesDirection)
            {
                var sef = Resources.Load<SpriteEffectAnima>(dir + "\\" + id);
                if (!sef) continue;
                _animaLibary[id] = sef;
                return sef;
            }

            throw new Exception($"加载动画 {id} 失败，没有这个动画文件。");
        }

        /// <summary>
        /// 内部维护的特效数据
        /// </summary>
        private struct Effect
        {
            public readonly SpriteRenderer Render;
            public readonly GameObject GameObject;
            public readonly Transform Transform;
            public Sprite[] Frames;
            public float FramesInterval;
            public int CurrentFrame;
            public int AnimaLength;
            public float Timer;
            public float Speed;
            public bool Loop;

            public Effect(SpriteRenderer render)
            {
                Render = render;
                GameObject = render.gameObject;
                Transform = render.transform;
                Frames = null;
                FramesInterval = 0.03333f;
                CurrentFrame = 0;
                AnimaLength = 0;
                Timer = 0;
                Speed = 1;
                Loop = false;
            }
        }
    }
}